// MainWindow.cs
// 
// All the autors on autors.txt
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Threading;
using Gtk;
using Gdk;

namespace Zencoder
{	
	public class MainWindow 
	{
		int bitrateAudio, bitrateVideo;
		bool converting, pulsing;
		
		private Conversion conversion;
		
		Queue<Conversion> queue;
		
		
		
		private Gdk.Pixbuf icon;
		private Gtk.TargetEntry [ ] targetTable;
		
		private Gtk.Window window;
		private Gtk.VBox mainVBox;
		
		//VBox first box
		private Gtk.MenuBar menuBar;
		private Gtk.Menu menuFile;
		private Gtk.MenuItem menuItemFileExit;
		private Gtk.MenuItem menuItemFile;
		private Gtk.Menu menuHelp;
		private Gtk.MenuItem menuItemHelp;
		private Gtk.MenuItem menuItemHelpAbout;
		
		//VBox second box
		private Gtk.HBox mainHbox;
		
		private Gtk.VBox leftVBox, rightVBox;
		
		//leftVBox
		private Gtk.HBox dropHbox;
		private Gtk.EventBox image;
		private Gtk.Label labelDrop;
		private Gtk.Frame framePreferences;
		private Gtk.Frame frameStatus;
		private Gtk.VBox preferencesVBox, statusVBox;
		private Gtk.HBox spinVideoHbox, spinAudioHbox, statusButtonHBox;
		private Gtk.CheckButton checkAudio;
		private Gtk.Label labelVideoBitrate, labelAudioBitrate, labelStatus;
		private Gtk.SpinButton spinButtonAudio, spinButtonVideo;
		private Gtk.ProgressBar progressBar;
		private Gtk.Button buttonStartSkip;
		
		//rightVBox
		private Gtk.TreeView tree;
		private Gtk.ListStore listStore;
		private Gtk.ScrolledWindow scrollWin;
		
		public MainWindow()
		{
			string title = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name;
			this.window = new Gtk.Window(title);
			if (window.Screen.RgbaColormap != null)
				Gtk.Widget.DefaultColormap = window.Screen.RgbaColormap;
			this.Constructors();
			this.window.Icon = this.icon;
			this.MenuBar();
			this.DropBox();
			this.Preferences();
			this.Status();
			this.RightBox();
			this.MainBoxes();
			this.Events();
			this.GuiSettings();
			this.window.ShowAll();
		}
		
		private void Constructors()
		{
			this.icon = new Gdk.Pixbuf(null, "zen.png");
			this.mainVBox = new VBox();
			this.menuBar = new MenuBar();
			this.menuFile = new Menu();
			this.menuHelp = new Menu();
			this.menuItemFile = new MenuItem("File");
			this.menuItemFileExit = new MenuItem("Exit");
			this.menuItemHelp = new MenuItem("Help");
			this.menuItemHelpAbout = new MenuItem("About");
			this.mainHbox = new HBox();
			this.leftVBox = new VBox();
			this.dropHbox = new HBox();
			this.image = new EventBox();
			this.labelDrop = new Label("Drop videos here");
			this.framePreferences = new Frame("Preferences");
			this.frameStatus = new Frame("Status");	
			this.preferencesVBox = new VBox();
			this.spinAudioHbox = new HBox();
			this.spinVideoHbox = new HBox();
			this.checkAudio = new CheckButton("Enable audio encoding");
			this.spinButtonAudio = new SpinButton(0f, 320f, 1f);
			this.spinButtonVideo = new SpinButton(0f, 2000f, 1f);
			this.labelVideoBitrate = new Gtk.Label("Video bitrate:");
			this.labelAudioBitrate = new Gtk.Label("Audio bitrate:");
			this.labelStatus = new Gtk.Label("Status");
			this.statusButtonHBox = new HBox();
			this.progressBar = new ProgressBar();
			this.statusVBox = new VBox();
			this.buttonStartSkip = new Button("Start");
			this.rightVBox = new VBox();
			this.tree = new TreeView();
			this.listStore = new ListStore(typeof(string));
			scrollWin = new Gtk.ScrolledWindow();
			targetTable = new TargetEntry [ ] { new TargetEntry("text/uri-list",0,0),};
			this.queue = new Queue<Conversion>();
		}
		
		private void GuiSettings()
		{
			uint borderwidth = 6;
			int spacing = 6;
			this.mainHbox.BorderWidth = borderwidth;
			this.dropHbox.BorderWidth = borderwidth;
			this.spinVideoHbox.BorderWidth = borderwidth;
			this.spinAudioHbox.BorderWidth = borderwidth;
			this.statusButtonHBox.BorderWidth = 0;
			this.leftVBox.BorderWidth = borderwidth;
			this.rightVBox.BorderWidth = borderwidth;
			this.preferencesVBox.BorderWidth = borderwidth;
			this.statusVBox.BorderWidth = borderwidth;
			
			this.mainHbox.Spacing = spacing;
			this.dropHbox.Spacing = spacing;
			this.spinVideoHbox.Spacing = spacing;
			this.spinAudioHbox.Spacing = spacing;
			this.statusButtonHBox.Spacing = spacing;
			this.leftVBox.Spacing = spacing;
			this.rightVBox.Spacing = spacing;
			this.preferencesVBox.Spacing = spacing;
			this.statusVBox.Spacing = spacing;
			this.labelStatus.Ellipsize = Pango.EllipsizeMode.Middle;
			this.spinButtonVideo.Value = 800f;
			this.spinButtonAudio.Value = 128f;
			this.labelAudioBitrate.Sensitive = false;
			this.spinButtonAudio.Sensitive = false;
			this.labelStatus.SetSizeRequest(120,-1);
			
			/*
			videoBitrate = videoSpinButton.ValueAsInt;
			
			*/
			
		}
		
		private void MenuBar()
		{
			this.menuItemFile.Submenu = this.menuFile;
			this.menuFile.Append(this.menuItemFileExit);
			this.menuItemHelp.Submenu = this.menuHelp;
			this.menuHelp.Append(this.menuItemHelpAbout);			
			this.menuBar.Append(this.menuItemFile);
			this.menuBar.Append(this.menuItemHelp);
		}
		
		private void DropBox()
		{
			this.image.Add(new Gtk.Image(icon));
			this.dropHbox.Add(image);
			this.dropHbox.Add(this.labelDrop);
		}

		private void Preferences()
		{
			this.spinVideoHbox.Add(this.labelVideoBitrate);
			this.spinVideoHbox.Add(this.spinButtonVideo);
			this.spinAudioHbox.Add(this.labelAudioBitrate);
			this.spinAudioHbox.Add(this.spinButtonAudio);
			this.preferencesVBox.Add(this.spinVideoHbox);	
			this.preferencesVBox.Add(this.checkAudio);
			this.preferencesVBox.Add(this.spinAudioHbox);
			this.framePreferences.Add(this.preferencesVBox);
		}
		
		private void Status()
		{
			this.statusButtonHBox.Add(this.labelStatus);
			this.statusButtonHBox.Add(this.buttonStartSkip);
			this.statusVBox.Add(statusButtonHBox);
			this.statusVBox.Add(this.progressBar);
			this.frameStatus.Add(this.statusVBox);
		}
		
		private void RightBox()
		{
			this.tree.Model = this.listStore;
			this.tree.HeadersVisible = true;
			this.tree.AppendColumn("Queue", new CellRendererText(), "text", 0);
			this.scrollWin.ShadowType = ShadowType.In;
			this.scrollWin.Add(tree);
			this.rightVBox.SetSizeRequest(200, -1);
		}
		
		private void MainBoxes()
		{
			this.leftVBox.Add(this.dropHbox);
			this.leftVBox.Add(this.framePreferences);
			this.leftVBox.Add(this.frameStatus);
			this.rightVBox.Add(this.scrollWin);
			this.mainHbox.Add(this.leftVBox);
			this.mainHbox.Add(this.rightVBox);
			this.mainVBox.Add(this.menuBar);
			this.mainVBox.Add(this.mainHbox);
			this.window.Add(this.mainVBox);
		}
		
		private void Events()
		{
			this.window.DeleteEvent += OnWindowDelete;
			this.menuItemFileExit.Activated += new EventHandler (OnExitItemActivate);
			this.menuItemHelpAbout.Activated += new EventHandler(OnAboutItemActivate);
			
			
			Gtk.Drag.DestSet(this.dropHbox, DestDefaults.All, targetTable, Gdk.DragAction.Copy);
			this.dropHbox.DragDataReceived += DataReceived;
			this.checkAudio.Toggled += new EventHandler(OnAudioCheckToggled);
			this.spinButtonVideo.ValueChanged += new EventHandler(GetVideoSpinButtonValue);
			this.spinButtonAudio.ValueChanged += new EventHandler(GetAudioSpinButtonValue);
			this.buttonStartSkip.Clicked += new EventHandler(OnStartSkipButtonClicked);
		}
		
		private void DataReceived(object o, DragDataReceivedArgs args)
		{
			bool success = false;
			string data = System.Text.Encoding.UTF8.GetString(args.SelectionData.Data);

			switch(args.Info)
			{
				case 0:
					string [ ] uriList = Regex.Split(data, "\r\n");
					foreach(string uri in uriList)
					{
						if(uri.Length > 0)
						{
							String filename = uri.Substring(7);
							filename = System.Uri.UnescapeDataString(filename);
							Gtk.Application.Invoke(delegate {
								listStore.AppendValues(filename.Substring(filename.LastIndexOf("/")+1));
							});
							this.queue.Enqueue(new Conversion(filename, this.bitrateVideo));;
						}
					}
					success = true;
					break;
			}

			Gtk.Drag.Finish(args.Context, success, false, args.Time);
		}
		
		private bool PulseProgressbar()
		{
			if (this.pulsing)
			{
				Gtk.Application.Invoke(delegate {
					this.progressBar.Text = "Converting...";
					this.progressBar.Pulse();
				});
			}
			else
			{
				Gtk.Application.Invoke(delegate {
					this.progressBar.Text = "";
					this.progressBar.Fraction = 0;
				});
			}
			return pulsing;
		}
		
		private void OnStartSkipButtonClicked (object obj, EventArgs args)
		{
			if(this.converting)
				this.SkipConversion();
			else
				this.StartConversion();
		}

		private void StartConversion()
		{
			this.converting = true;
			this.conversion = this.queue.Dequeue();
			this.conversion.StartConversionEvent += new StartConversionEventHandler(OnStartConversion);
			this.conversion.StopConversionEvent += new StopConversionEventHandler(OnStopConversion);
			Thread tConversion = new Thread(this.conversion.DoConversion);
			this.pulsing = true;
			tConversion.Start();
			this.PulseProgressbar();
			
		}
		
		private void SkipConversion()
		{
			//to do skip conversing process
		}
		
		private void OnStartConversion(object obj, EventArgs args)
		{
			pulsing = true;
			GLib.Timeout.Add (100, new GLib.TimeoutHandler (PulseProgressbar));
			this.labelStatus.Text = conversion.Outputfilename;
			this.buttonStartSkip.Name = "Skip";
		}
		
		private void OnStopConversion(object obj, EventArgs args)
		{
			this.labelStatus.Text = ("Idle");
			this.buttonStartSkip.Name = "Start";
			Gtk.TreeIter iter;
			this.listStore.GetIterFirst (out iter);
			this.listStore.Remove(ref iter);
		}
		
		private void GetVideoSpinButtonValue(object source, System.EventArgs args)
		{
			SpinButton videoSpinButton = source as SpinButton;
			this.bitrateVideo = videoSpinButton.ValueAsInt;
		}
		
		private void GetAudioSpinButtonValue(object source, System.EventArgs args)
		{
			SpinButton audioSpinButton = source as SpinButton;
			this.bitrateAudio = audioSpinButton.ValueAsInt;
		}
		
		private void OnAudioCheckToggled (object obj, EventArgs args)
		{
			if (this.checkAudio.Active)
			{
				Gtk.Application.Invoke(delegate {
					this.labelAudioBitrate.Sensitive = true;
					this.spinButtonAudio.Sensitive = true;
				});
			}
			else
			{
				Gtk.Application.Invoke(delegate {
					this.labelAudioBitrate.Sensitive = false;
					this.spinButtonAudio.Sensitive = false;
				});
			}
		}
		
		private void OnWindowDelete(object o, EventArgs args)
		{
			Application.Quit();
		}

		private void OnExitItemActivate(object o, EventArgs args)
		{
			Application.Quit ();
		}
		
		private void OnAboutItemActivate(object o, EventArgs args)
		{
			AboutDialog dialog = new AboutDialog (this.icon);
			//todo add aboutdialog
		}
	}
}
